class MinigamePlayerController extends xPlayer;

var bool           HasInteractionEnabled;
var interaction    PlayerInteraction;

// Handle "death"
var bool           DeathCountdown;       // Did they "die"?  Then true
var float          DeathTimer;           // How much time left until respawning?
var float          DeathTimerStart;      // The time until "death" in seconds

// Pawn movement parameters
var rotator        DesiredPlayerRotation;
var rotator        ActualPlayerRotation;
var vector         DesiredPlayerLocation;
var vector         UseDesiredPlayerLocation; // Really just bools; 1 to use X, Y, or Z ... 0 otherwise
var float          PlayerPropelFactor;   // A constant that determines how fast a value of "1" propels them
var vector         PlayerAcceleration;   // Independent of Pawn.Acceleration
var vector         PlayerVelocity;       // Independent of Pawn.Velocity
var rotator        PlayerRotation;       // Independent of Pawn.Rotation

// Camera movement parameters
var vector         ActualCameraLocation;
var float          CameraDistanceFactor; // A constant that determines how far away from the player a value of "1" will set the camera
var float          ActualCameraDistance;
var float          DesiredCameraDistance;

// Camera rotation parameters
var rotator        DesiredCameraRotation;
var rotator        ActualCameraRotation;
var rotator        CameraRotationSpeed;
var float          CameraRotationMaximumSpeed;
var rotator        CameraRotationAcceleration;
var float          CameraRotationAccelerationFactor;
var float          CameraRotationSlowdownDistance;
var float          CameraRotationSizePerTick;

// Miscellaneous
var MinigameFlyingVolume CurrentFlyingVolume;  // The flying volume the player's currently in
var EffectSparkleTrail SparkleTrail;
var EffectAirDebris AirDebris;

event PlayerTick( float DeltaTime )
{

    local float MagicCameraSlowdownNumber;
    local rotator CameraTravelDistance;
    local vector TempLocation;

    if (DeathCountdown) {
        // If the player's been hit, countdown until they respawn at the beginning
        // so disable player movement
        SparkleTrail.Kill();
        if (DeathTimer > 0) {
            if (HasInteractionEnabled) {
                Pawn.SetDrawType(DT_None);
                Spawn(class'Enyara.EffectPetExplosion',,,Pawn.Location);
                Player.InteractionMaster.RemoveInteraction(PlayerInteraction);
                HasInteractionEnabled = false;
            }
            DeathTimer -= DeltaTime;
        } else {
            // AirDebris.Kill();
            DeathCountdown = false;
            Pawn.SetLocation(Location);
            PlayerVelocity = vect(0,0,0);
            PlayerAcceleration = vect(0,0,0);
            Pawn.SetPhysics(PHYS_None);
        }
        return;
    } else {
        if (!HasInteractionEnabled) {
            Pawn.SetDrawType(DT_Mesh);
            PlayerInteraction = Player.InteractionMaster.AddInteraction("Minigame.MinigamePlayerInteraction", Player);
            HasInteractionEnabled = true;
        }
        if (Pawn.Physics != PHYS_Flying) {
           ActualPlayerRotation = rot(0,0,0);
           ActualCameraRotation = rot(0,0,0);
           ActualCameraLocation = Pawn.Location;
           Pawn.SetPhysics(PHYS_Flying);
           Pawn.AirSpeed = 1024;

           // Now attach some emitter effects.  First, the sparkle trail
           SparkleTrail = Spawn (class'Enyara.EffectSparkleTrail',,,Pawn.Location);
           SparkleTrail.SetBase (Pawn);

           // Then, the air debris
           // AirDebris = Spawn (class'Minigame.EnyaraAirDebris',,,Pawn.Location);
           // AirDebris.SetBase (Pawn);
        }
        if (Pawn.PhysicsVolume.class!=class'MinigameFlyingVolume') {
            PlayerKill();
        }
    }

    // Update player velocity/location

    if (UseDesiredPlayerLocation != vect(0,0,0)) {

        TempLocation = Pawn.Location;
        if (UseDesiredPlayerLocation.X > 0) {
            PlayerAcceleration.X = 0;
            TempLocation.X += (DesiredPlayerLocation.X - TempLocation.X) * 2 * DeltaTime;
        }
        if (UseDesiredPlayerLocation.Y > 0) {
            PlayerAcceleration.Y = 0;
            TempLocation.Y += (DesiredPlayerLocation.Y - TempLocation.Y) * 2 * DeltaTime;
        }
        if (UseDesiredPlayerLocation.Z > 0) {
            PlayerAcceleration.Z = 0;
            TempLocation.Z += (DesiredPlayerLocation.Z - TempLocation.Z) * 2 * DeltaTime;
        }
        Pawn.SetLocation(TempLocation);

    }

    if (PlayerAcceleration == vect(0, 0, 0)) {
       // If they aren't hitting a key, start deteriorating their velocity
       PlayerVelocity *= 1 - (3 * DeltaTime);
    } else {
       // They ARE hitting a key, so add the acceleration to their velocity
       PlayerVelocity += PlayerAcceleration * (1600 * DeltaTime);
       PlayerVelocity = VClamp (PlayerVelocity, 0, Pawn.AirSpeed);
    }

    // Update player rotation

    ActualPlayerRotation += (DesiredPlayerRotation - ActualPlayerRotation) * 2 * DeltaTime;
    Pawn.SetRotation(ActualPlayerRotation);

    // Update camera rotation

    CameraTravelDistance = DesiredCameraRotation - ActualCameraRotation;
    CameraRotationAcceleration = RClamp(CameraTravelDistance, 0, 2);
    MagicCameraSlowdownNumber = FClamp (RSize(CameraTravelDistance) * CameraRotationMaximumSpeed / CameraRotationSlowdownDistance, 0, CameraRotationMaximumSpeed);
    if (RSize(CameraTravelDistance) < 16) {
        // If they're close enough, snap 'em into place!
        ActualCameraRotation = DesiredCameraRotation;
        CameraRotationAcceleration = rot(0,0,0);
        CameraRotationSpeed = rot(0,0,0);
    } else {
        CameraRotationSpeed += CameraRotationAcceleration;
        CameraRotationSpeed = RClamp (CameraRotationSpeed, 0, MagicCameraSlowdownNumber);
        ActualCameraRotation += CameraRotationSpeed * 128 * DeltaTime;
    }

    // Update camera location

    ActualCameraLocation += (Pawn.Location - ActualCameraLocation) * 8 * DeltaTime;
    ActualCameraDistance += (DesiredCameraDistance - ActualCameraDistance) * 2 * DeltaTime;

    if (DeathCountdown == true) {
        // If they "died," then disable movement
        Pawn.Velocity = vect(0,0,0);
    } else {
        // If they're with us, update the pawn's velocity
        Pawn.Velocity = (PlayerPropelFactor * CurrentFlyingVolume.PlayerVelocity + PlayerVelocity) >> ActualPlayerRotation;
    }

}

event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
{

    ViewActor = Self;
    bBehindView = true;

    CameraLocation = ActualCameraLocation - vector(ActualCameraRotation) * ActualCameraDistance * CameraDistanceFactor; // - vector(CameraTravelRotation) * CameraTravelDistance * CameraDistanceFactor;
    CameraRotation = ActualCameraRotation;

}

function bool PlayerKill () {

    if (CurrentFlyingVolume.Invincibility)
    return false;

    DeathTimer = DeathTimerStart;
    DeathCountdown = true;
    return true;
}

function rotator RClamp (rotator r, float min, float max) {

    local float size;
    size = RSize(r);

    if (size > max) return r * max / size;
    if (size < min) return r * min / size;
    return r;

}

function vector VClamp (vector v, float min, float max) {

    local float size;
    size = VSize(v);

    if (size > max) return v * max / size;
    if (size < min) return v * min / size;
    return v;

}

function float RSize (rotator r) {
   return sqrt(square(r.Pitch)+square(r.Yaw)+square(r.Roll));
}

static function quat RotateQuat (quat q, rotator r) {

    local quat tempQuat;

    // Roll
    tempQuat = QuatFromAxisAndAngle( vect(1,0,0), 1.57085331 / 16384 * r.Roll );
    q = QuatProduct(q, tempQuat);

    // Yaw
    tempQuat = QuatFromAxisAndAngle( vect(0,0,1), 1.57085331 / 16384 * r.Yaw );
    q = QuatProduct(q, tempQuat);

    // Pitch
    tempQuat = QuatFromAxisAndAngle( vect(0,1,0), 1.57085331 / 16384 * r.Pitch );
    q = QuatProduct(q, tempQuat);

    return q;

}

function SetPawnClass(string inClass, string inCharacter)
{
    // local class<MinigamePawn> pClass;
    PawnClass = class'MinigamePawn';
}

defaultproperties {

     InputClass = class'MinigamePlayerInput' // I had to make a new input class to disable traditional movement
     PawnClass = class'Enyara.MinigamePawn'
     HasInteractionEnabled = false

     DeathTimer = 0;
     DeathTimerStart = 3;

     PlayerPropelFactor = 768;
     CameraDistanceFactor = 256;

     CameraRotationMaximumSpeed = 128;

}
